﻿using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Daq.Services;
using Daq.Views;
using Microsoft.VisualBasic.FileIO;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Telerik.Charting;
using Telerik.Documents.SpreadsheetStreaming;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.FieldList;
using SearchOption = System.IO.SearchOption;

namespace Daq.ViewModels
{
    public class SelectedDataRange
    {
        public long Start { get; set; }
        public long End { get; set; }
    }
    public struct ChartData
	{
		public float Value;
		public DateTime Time;
	}
	public partial class ChartChannelDataModel : ObservableObject
	{
		[ObservableProperty]
		private string name;

		[ObservableProperty]
		private List<ChartData> values = [];

		//[ObservableProperty]
		//private ISeries[] series;
    }

	public partial class ChartSeriesModel : ObservableObject
	{
        [ObservableProperty]
        private ObservableCollection<ChartChannelDataModel> chartChannels = [];

		public string SeriesType => "Line";
	}

	public struct ChannelInfo
	{
		public string Name { get; set; }
		public DaqColor Color { get; set; }
	}

	public enum TrendDataType
	{
        [Display(Name = "触发")]
        Trigger,
        [Display(Name = "最大值")]
        Max,
        [Display(Name = "最小值")]
        Min,
        [Display(Name = "平均值")]
        Average,
	}


	public class TrendData
    {
        public DateTime StartTime { get; set; }
        private object valueLockObj = new();
        private double[]? values;
        public double[]? Values
        {
            get
            {
                lock (valueLockObj)
                {
                    return values;
                }
            }
            set
            {
                lock (valueLockObj)
                {
                    values = value;
                }
            }
        }

        private object triggerCountsLockObj = new();
        private double[]? triggerCounts;
        public double[]? TriggerCounts
        {
            get
            {
                lock (triggerCountsLockObj)
                {
                    return triggerCounts;
                }
            }
            set
            {
                lock (triggerCountsLockObj)
                {
                    triggerCounts = value;
                }
            }
        }
    }
    public partial class TrendDataPlotSettings : ObservableObject
    {
        [ObservableProperty]
        private bool useAbsolutelyTime;
    }

	public partial class TrendChartViewModel : ObservableObject
	{
        [ObservableProperty]
        private double[] _xData;
        [ObservableProperty]
        private double[] _yData;

        public void AppendData(double newX, double newY)
        {
            // 实际场景中可使用环形缓冲区或预分配大数组避免频繁扩容
            Array.Resize(ref _xData, XData.Length + 1);
            Array.Resize(ref _yData, YData.Length + 1);
            XData[^1] = newX;
            YData[^1] = newY;
        }

        // 批量加载大数据（初始化时使用）
        public void LoadLargeData()
        {
            // 模拟100万条数据（实际可能来自文件/数据库）
            int count = 1_000_000;
            _xData = new double[count];
            _yData = new double[count];
            for (int i = 0; i < count; i++)
            {
                _xData[i] = i * 0.01;
                _yData[i] = Math.Sin(_xData[i]) + Random.Shared.NextDouble() * 0.1;
            }

            // 全量数据更新通知
            OnPropertyChanged(nameof(XData));
            OnPropertyChanged(nameof(YData));
        }
    }

    public partial class DataManagerViewModel : ObservableRecipient
    {
		[ObservableProperty]
		private string? name;

		[ObservableProperty]
		private ObservableCollection<DateTime> blackoutDates = [];

		[ObservableProperty]
		private List<string> availableDirectories = [];

		[ObservableProperty]
		private DateTime displayDateStart = DateTime.Today;

		[ObservableProperty]
		private DateTime displayDateEnd = DateTime.Today;

		[ObservableProperty]
		private long databaseCount;

		[ObservableProperty]
		private List<ChannelInfo> channelBriefInfos;

		[ObservableProperty]
		private ObservableCollection<string> channelNames1 = [];

		[ObservableProperty]
		private ObservableCollection<ChartChannelDataModel> chartChannels = [];

		private SettingsService settings;
		private DataService dataService;

        [ObservableProperty]
        private TrendData trendData = new();
        [ObservableProperty]
        private TrendDataPlotSettings trendDataPlotSettings = new();
        [ObservableProperty]
        private long selectedDataRangeStart;
        [ObservableProperty]
        private long selectedDataRangeEnd;
        [ObservableProperty]
        private MetaData? currentMetaData;
        [ObservableProperty]
        private DeviceService? deviceService;

		[ObservableProperty]
		private DateTime trendStartTime;
		[ObservableProperty]
		private DateTime trendEndTime;
		[ObservableProperty]
		private double[]? trendDataList;
		[ObservableProperty]
		private double[]? rawData;
		[ObservableProperty]
		private string? selectedTrendDataChannelName;
		[ObservableProperty]
		private TrendDataType trendDataType;
		public List<TrendDataType> TrendDataTypes => EnumHelper<TrendDataType>.ToList();

        private readonly IDialogService dialogService;
        public DataManagerViewModel(SettingsService settings, DataService data, DeviceService deviceService, IDialogService dialogService)
		{
            this.settings = settings;
			dataService = data;
			this.dialogService = dialogService;
            DataRootDirectory = settings.SystemSettings.Instance.DataDirectory;
			DeviceService = deviceService;
            IsActive = true;
            SelectedChannelNames.CollectionChanged += SelectedChannelNames_CollectionChanged;
        }

        private void SelectedChannelNames_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            QueryDataCommand.NotifyCanExecuteChanged();
            ExportDataCommand.NotifyCanExecuteChanged();
        }

        protected override void OnActivated()
        {
            WeakReferenceMessenger.Default.Register<SelectedDataRange, string>(this, "updateSelectedDataRange", (obj, range) => { SelectedDataRangeStart = range.Start; SelectedDataRangeEnd = range.End; });
        }

        protected override void OnDeactivated()
        {
            base.OnDeactivated();
            WeakReferenceMessenger.Default.Unregister<SelectedDataRange>(this);
        }

		[ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(ExportDataCommand))]
        private TimeSpan databaseStartTime;

		[ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(ExportDataCommand))]
        private TimeSpan databaseEndTime;
        partial void OnDatabaseEndTimeChanged(TimeSpan value)
        {
            if (DatabaseStartTime > value)
            {
                DatabaseStartTime = value;
            }
        }

        partial void OnDatabaseStartTimeChanged(TimeSpan value)
        {
            if(DatabaseEndTime < value)
			{
				DatabaseEndTime = value;
            }
        }

        partial void OnSelectedTrendDataChannelNameChanged(string? value)
        {
			if(CurrentMetaData != null && !string.IsNullOrWhiteSpace(value))
			{
                TrendDataList = GetTrendData(CurrentMetaData, value, TrendDataType);
            }
        }

        private DateTime selectedDate;
		public DateTime SelectedDate 
		{ 
			get => selectedDate;
			set
			{
				SetProperty(ref selectedDate, value);
			}
		}
		private DateTime selectedTime;
		public DateTime SelectedTime
		{ 
			get => selectedTime;
			set
			{
				Debug.WriteLine(value);
				SetProperty(ref selectedTime, value);
			}
		}

		[ObservableProperty]
		[NotifyCanExecuteChangedFor(nameof(QueryDataCommand))]
		[NotifyCanExecuteChangedFor(nameof(PreviewDataCommand))]
		[NotifyCanExecuteChangedFor(nameof(ExportDataCommand))]
		private string? selectedDirectoryName;

		[ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(QueryDataCommand))]
        [NotifyCanExecuteChangedFor(nameof(ExportDataCommand))]
        private ObservableCollection<string> selectedChannelNames = [];

		[ObservableProperty]
        private string? selectedChannelName;

		//[ObservableProperty]
		//private List<string>? channelNames;

		[ObservableProperty]
		[NotifyCanExecuteChangedFor(nameof(GenerateReportCommand))]
		private DataTable? data;

		[ObservableProperty]
		private bool isLoading;

		[ObservableProperty]
		private string? dataRootDirectory;
		[ObservableProperty]
		private string? newDataRootDirectory;

		private static readonly string rootDirectory = @"C:\ProgramData\DAQ";
		//private static readonly string dataRootDirectory = $"{rootDirectory}/data/";
		private static string filePath;
		//private static SQLiteHelper db;
		private static readonly string dateTimeFormat = "yyyyMMdd_HHmmss";
		private List<DateTime> availableDateTimes;


		public DataManagerViewModel()
		{

        }

		[RelayCommand]
		private async Task FileSelected(object obj)
		{
			await Task.FromResult(0);
			return;
			//IsLoading = true;
			//if(obj is DateTime time)
			//{
			//	var info = await ReadDataInfo(time);
			//	DatabaseCount = info.Item1;
			//	DatabaseStartTime = info.Item2;
			//	DatabaseEndTime = info.Item3;
			//	Debug.WriteLine(info);
			//	SelectedTime = time;
			//	//await ReadDataAsync(time);
			//	Debug.WriteLine("读取数据");
			//	(Data, _) = await ReadDataAsync(time, 0, 1000);//await ReadDataAsync(time);
   //             Debug.WriteLine("读取完成");
   //         }
			//IsLoading = false;
		}

		public async Task RefreshBlackoutDatesAysnc()
		{
			await RefreshBlackoutDates();
		}

		private async Task<List<string>?> GetDatabaseFiles(string directoryPath)
		{
            // 验证输入参数
            if (string.IsNullOrWhiteSpace(directoryPath))
			{
				return null;
			}
			return await Task.Run(() =>
			{
				if (!Directory.Exists(directoryPath))
				{
					return null;
				}

				try
				{
					List<string> files = [.. Directory.GetFiles(directoryPath, "*.dxd", SearchOption.AllDirectories)];
					return files;
				}
				catch (Exception ex)
				{
					Debug.WriteLine(ex.Message);
				}
				return null;
			});
        }

        partial void OnSelectedDirectoryNameChanged(string? value)
        {
			if (string.IsNullOrWhiteSpace(value)) return;
			Task.Run(async() =>
			{
                CurrentMetaData = await dataService.ReadMetaData(DataRootDirectory + "\\" + value);
				if(CurrentMetaData == null || CurrentMetaData.Channels == null)
				{
					ChannelNames = null;
					return;
                }
				await App.Current.Dispatcher.BeginInvoke(() =>
				{
					ChannelNames = [.. CurrentMetaData.Channels.Select(ch =>  ch.Info.Name ).ToArray()];
					if(ChannelNames.Count > 0)
					{
						SelectedTrendDataChannelName = ChannelNames[0];
					}
					
				});
            });
        }

        partial void OnTrendDataTypeChanged(TrendDataType value)
        {
            if (CurrentMetaData != null && !string.IsNullOrWhiteSpace(SelectedTrendDataChannelName))
            {
                TrendDataList = GetTrendData(CurrentMetaData, SelectedTrendDataChannelName, value);
            }
        }

        partial void OnSelectedTrendDataChannelNameChanged(string? oldValue, string? newValue)
        {
            if (CurrentMetaData != null && !string.IsNullOrWhiteSpace(newValue))
            {
                TrendDataList = GetTrendData(CurrentMetaData, newValue, TrendDataType);
            }
        }

		[RelayCommand]
		private async Task ReadRawData()
		{
			var result = await QueryData(SelectedTrendDataChannelName, DatabaseStartTime, DatabaseEndTime);
			RawData = result;
        }

        [RelayCommand]
		private async Task RefreshFiles()
		{
            IsLoading = true;
            AvailableDirectories = await GetAvailableDirectories();
            IsLoading = false;
        }

        [RelayCommand]
		private async Task RefreshBlackoutDates()
		{
			//IsLoading = true;
			//availableDateTimes = await GetAvailableDateTimes();
			//if (availableDateTimes == null || availableDateTimes.Count == 0) { return; }

			//DateTime min = availableDateTimes[0].Date;
			//DateTime max = availableDateTimes[0].Date;
			//foreach (var time in availableDateTimes)
			//{
			//	var date = time.Date;
			//	if (date < min)
			//	{
			//		min = date;
			//	}
			//	if (date > max)
			//	{
			//		max = date;
			//	}
			//}
			//DisplayDateEnd = max;
			//DisplayDateStart = min;
			//ObservableCollection<DateTime> dates = [];
			//for (DateTime date = min; date <= max; date = date.AddDays(1))
			//{
			//	if (!availableDateTimes.Any(t => t.Date == date))
			//	{
			//		dates.Add(date);
			//	}
			//}
			//BlackoutDates = dates;		
			//if (availableDateTimes.Any())
			//{
			//	if (SelectedDate == DateTime.MinValue)
   //             {
   //                 SelectedDate = availableDateTimes.Last().Date;
   //             }
			//}
   //         RefreshAvailabeFiles();
   //         IsLoading = false;
		}

		private DataTable dataInfo;


		private string selectedFilePath => DataRootDirectory + '\\' + SelectedDirectoryName;


		public List<string> SortChannelNames()
		{
			List<string> names = [];
			if (CurrentMetaData != null && CurrentMetaData.Channels != null)
			{
				foreach (var ch in CurrentMetaData.Channels)
				{
					if (ch == null) continue;
					if (!SelectedChannelNames.Contains(ch.Info.Name)) continue;
					names.Add(ch.Info.Name);
				}
			}
			return names;
        }


		private double[]? GetTrendData(MetaData metaData, string channelName, TrendDataType type)
		{
            if (metaData == null || metaData.Channels == null) return null;
			if (string.IsNullOrWhiteSpace(channelName)) return null;
			var ch = metaData.Channels.FirstOrDefault(ch => ch.Info.Name == channelName);
			if (ch == null) return null;

			int digits = ch.Info.Digits;
			int count = ch.Properties.Count;
			double[] data = new double[count];
			switch(type)
			{
				case TrendDataType.Min:
                    for (int i = 0; i < count; i++)
                    {
                        data[i] = (double)ch.Properties[i].Min / Math.Pow(10, digits);
                    }
                    break;
				case TrendDataType.Max:
                    for (int i = 0; i < count; i++)
                    {
                        data[i] = (double)ch.Properties[i].Max / Math.Pow(10, digits);
                    }
                    break;
				case TrendDataType.Average:
                    for (int i = 0; i < count; i++)
                    {
                        data[i] = ch.Properties[i].Average / Math.Pow(10, digits);
                    }
                    break;
				case TrendDataType.Trigger:
                    for (int i = 0; i < count; i++)
                    {
                        data[i] = ch.Properties[i].TriggerCount;
                    }
                    break;
			}
			
			return data;
		}

		private bool CanPreviewData => !string.IsNullOrWhiteSpace(SelectedDirectoryName);

		[RelayCommand(CanExecute = nameof(CanPreviewData))]
		private async Task PreviewData()
		{
			IsLoading = true;
			CurrentMetaData = await dataService.ReadMetaData(selectedFilePath);
			IsLoading = false;
			if(CurrentMetaData != null && CurrentMetaData.Channels != null)
			{
                ChannelNames = [.. CurrentMetaData.Channels.Select(t => t.Info.Name).ToArray()];
				if(ChannelNames.Count > 0)
				{
					SelectedTrendDataChannelName = ChannelNames[0];
				}
            }
			else
			{
				ChannelNames = null;
			}
        }

		private async Task<double[]?> QueryData(string channelName, TimeSpan startTime, TimeSpan endTime)
		{
			return await dataService.Query(selectedFilePath, channelName, (int)startTime.TotalMinutes, (int)endTime.TotalMinutes, CurrentMetaData);


			return null;
		}


		[RelayCommand(CanExecute = nameof(CanQueryData))]
		private async Task QueryData()
		{
			IsLoading = true;
			DatabaseCount = 0;
            CurrentMetaData = await dataService.ReadMetaData(selectedFilePath);
			TrendStartTime = DateTime.MinValue;
			TrendEndTime = TrendStartTime + TimeSpan.FromSeconds(CurrentMetaData.Channels[0].Properties.Sum(p => p.SampleCount) / 1000);
			

            List<string> names = SortChannelNames();
			if(CurrentMetaData != null && CurrentMetaData.Channels != null)
			{
				foreach(var ch in CurrentMetaData.Channels)
				{
					if (ch == null) continue;
					if (!SelectedChannelNames.Contains(ch.Info.Name)) continue;

                    long count = 0;
                    bool first = true;
                    foreach (var property in ch.Properties)
                    {
                        count += property.SampleCount;
                        if (first)
                        {
                            DataPageSize = property.SampleCount;
                            first = false;
                        }
                    }
                    if (count == 0) goto exit;
                    DatabaseCount = count;
                    break;
                }
            }
            Data?.Dispose();
            Data = await QueryData(0, names);

        exit:
			IsLoading = false;
		}

		[ObservableProperty]
		private double exportDataProgress;

		[RelayCommand(CanExecute = nameof(CanExportData))]
        private async Task ExportData()
		{
			if (string.IsNullOrWhiteSpace(SelectedDirectoryName)) return;
			Progress<double> progress = new Progress<double>(p=>ExportDataProgress = p);
			await ExportData(SelectedDirectoryName, SelectedChannelNames.ToList(), (int)DatabaseStartTime.TotalMinutes, (int)DatabaseEndTime.TotalMinutes, progress);
		}
        private static string EscapeCsvValue(string value)
        {
            if (string.IsNullOrEmpty(value))
                return string.Empty;

            // 如果值包含逗号、引号或换行符，需要用引号括起来
            bool needsQuotes = value.Contains(",") || value.Contains("\"") || value.Contains("\n");

            if (needsQuotes)
            {
                // 替换双引号为两个双引号
                value = value.Replace("\"", "\"\"");
                return $"\"{value}\"";
            }
            return value;
        }


        [ObservableProperty]
		private int dataPageSize;

		[ObservableProperty]
		private int dataCount;

		[ObservableProperty]
		private ObservableCollection<string>? channelNames;

        [RelayCommand]
		private async Task ChangePageIndex(object parameter)
		{
			if (CurrentMetaData == null || CurrentMetaData.Channels == null) return;
            IsLoading = true;
            if (parameter is PageIndexChangedEventArgs e)
			{
				var names = SortChannelNames();
				Data?.Dispose();
                Data = await QueryData(e.NewPageIndex, names);
    //            DataTable dt = new();
    //            var ch = CurrentMetaData.Channels.FirstOrDefault(ch => ch.Info.Name == SelectedChannelName);
				//if (ch == null) goto exit;
    //            string columnName = $"{ch.Info.Name}({ch.Info.Unit})";
    //            dt.Columns.Add("时间");
    //            dt.Columns.Add(columnName);
				//var startTime = CurrentMetaData.StartTime;
				//var samplesPerSecond = CurrentMetaData.SamplesPerSecond;
				//if (samplesPerSecond == 0) samplesPerSecond = 1000;
    //            var rawData = await dataService.Query(selectedFilePath, ch.Info.Name, e.NewPageIndex, e.NewPageIndex, CurrentMetaData);
				//if (rawData == null) goto exit;
    //            for (int i = 0; i < rawData.Length; i++)
    //            {
    //                var row = dt.Rows.Add(rawData.Length);
    //                row[columnName] = rawData[i];
				//	row["时间"] = startTime.AddSeconds(i / samplesPerSecond);
    //            }
    //            Data = dt;
            }
		exit:
			IsLoading = false;
		}

		private async Task<DataTable?> QueryData(TimeSpan startTime, TimeSpan endTime, List<string> channelNames)
		{
			//if (startTime > endTime) return null;
			//int startMinute = (int)startTime.TotalMinutes;
			//int endMinute = (int)endTime.TotalMinutes;

			return null;
        }

        private async Task<DataTable?> QueryData(int pageIndex, List<string> channelNames)
		{
            if (CurrentMetaData == null || CurrentMetaData.Channels == null) return null;
			if (channelNames.Count == 0) return null;

            DataTable dt = new();
            var ch = CurrentMetaData.Channels.FirstOrDefault(ch => ch.Info.Name == SelectedChannelNames[0]);
            if (ch == null) goto exit;
            //string columnName = $"{ch.Info.Name}({ch.Info.Unit})";
            dt.Columns.Add("序号");
            dt.Columns.Add("时间");
            var startTime = CurrentMetaData.StartTime;
            var samplesPerSecond = CurrentMetaData.SamplesPerSecond;
            if (samplesPerSecond == 0) samplesPerSecond = 1000;

			bool first = true;
			foreach(var name in channelNames)
			{
                dt.Columns.Add(name);
            }
            foreach (var name in channelNames)
            {
				Debug.WriteLine($"查询{name} - {pageIndex}");
                var rawData = await dataService.Query(selectedFilePath, name, pageIndex, pageIndex, CurrentMetaData);
				if (rawData == null) continue;
                while(dt.Rows.Count < rawData.Length)
				{
                    dt.Rows.Add();
                }

                for (int i = 0; i < rawData.Length; i++)
                {
                    if (first)
                    {
                        int index = samplesPerSecond * pageIndex * 60 + i;
                        dt.Rows[i]["序号"] = index + 1;
                        dt.Rows[i]["时间"] = startTime.AddSeconds(index / samplesPerSecond);
                    }
                    dt.Rows[i][name] = rawData[i];
                }
                first = false;
            }

            ////var rawData = await dataService.Query(selectedFilePath, ch.Info.Name, pageIndex, pageIndex, CurrentMetaData);
            //if (rawData == null) goto exit;
            //for (int i = 0; i < rawData.Length; i++)
            //{
				
            //    var row = dt.Rows.Add(rawData.Length);
            //    row[columnName] = rawData[i];
            //    int index = samplesPerSecond * pageIndex + i;
            //    row["序号"] = index + 1;
            //    row["时间"] = startTime.AddSeconds(index / samplesPerSecond);
            //}
		exit:
			return dt;
        }

		private bool CanQueryData => !string.IsNullOrWhiteSpace(SelectedDirectoryName) && SelectedChannelNames.Count > 0;
		private bool CanExportData => !string.IsNullOrWhiteSpace(SelectedDirectoryName) && SelectedChannelNames.Count > 0;
            //&& SelectedChannelNames != null
			//;

        [RelayCommand]
		private async Task ChangeDataRootDirectory()
		{
            OpenFolderDialog openFolderDialog = new OpenFolderDialog();
            openFolderDialog.ShowDialog();
            var name = openFolderDialog.FolderName;
            if (!string.IsNullOrWhiteSpace(name))
            {
                DataRootDirectory = name;
            }
			await RefreshFiles();
        }

		private void RefreshAvailabeFiles()
		{
			//if (availableDateTimes == null)
			//{
			//	AvailableFiles = [];
			//	return;
			//}
			//AvailableFiles = availableDateTimes.Where(t => t.Date == SelectedDate).ToList();
			//SelectedTime = AvailableFiles.FirstOrDefault();
		}
		public async Task<List<string>> GetAvailableDirectories()
		{
			if (string.IsNullOrWhiteSpace(DataRootDirectory)) return [];
            return await Task.Run(() =>
			{
				try
				{
                    List<DateTime> dateTimes = [];
                    DirectoryInfo di = new(DataRootDirectory);
                    var directories = di.GetDirectories();
                    List<string> fileNames = [];
                    foreach (var dir in directories)
                    {
                        try
                        {
                            DirectoryInfo subDirectory = new(dir.FullName);
                            var files = subDirectory.GetFiles();
                            if (files.Any(f => f.Extension == ".dxd"))
                            {
                                fileNames.Add(dir.Name);
                            }
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine($"遍历文件夹失败：{e.Message}");
                        }
                    }
					return fileNames;

                }
				catch
				{
                    return [];
                }
			});
		}

        public async Task ReadMetaData(string? fileName)
        {
            if (fileName == null) return;
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var data = await dataService.ReadMetaData(Path.Combine(DataRootDirectory, fileName));
            sw.Stop();
            Debug.WriteLine($"打开文件耗时{sw.ElapsedMilliseconds}ms");
            CurrentMetaData = data;
            if (data == null)
            {
                return;
            }

            TrendData.Values = null;
            TrendData.TriggerCounts = null;
            ChannelNames = null;
            TrendData.StartTime = data.StartTime;
            if (data.Channels == null || data.Channels.Count == 0)
            {
                return;
            }

            ChannelNames = [..data.Channels.Select(t =>  t.Info.Name).ToArray()];
            WeakReferenceMessenger.Default.Send(new TrendDataUpdatedMessage { });
        }
        private async Task GenerateDocument(string filePath)
		{
			if (Data == null) return;
			if (Data.Columns.Count == 0) return;
			await Task.Run(() =>
			{
				try
				{
					using (FileStream stream = File.OpenWrite(filePath))
					{
						using (IWorkbookExporter workbook = SpreadExporter.CreateWorkbookExporter(SpreadDocumentFormat.Xlsx, stream))
						{
							using (IWorksheetExporter worksheet = workbook.CreateWorksheetExporter("数据"))
							{
								using (IRowExporter row = worksheet.CreateRowExporter())
								{
									foreach (var column in Data.Columns)
									{
										using (ICellExporter cell = row.CreateCellExporter())
										{
											cell.SetValue(column.ToString());
										}
									}
								}

								foreach (DataRow row in Data.Rows)
								{
									if (row is DataRow data)
									{
										using (IRowExporter rowData = worksheet.CreateRowExporter())
										{
											foreach (var column in data.ItemArray)
											{
												using (ICellExporter cell = rowData.CreateCellExporter())
												{
													if (column is double doubleValue)
													{
														cell.SetValue(doubleValue);
													}
													else if (column is DateTime time)
													{
														cell.SetValue(time.ToString("yyyy-MM-dd HH:mm:ss.fff"));
													}
													else if (column is int intValue)
													{
														cell.SetValue(intValue);
													}
													else if (column is Int64 int64Value)
													{
														cell.SetValue(int64Value);
													}
													else
													{
														cell.SetValue(column == null ? string.Empty : column.ToString());
													}
												}
											}
										}
									}
								}
							}
						}
					}
					//XlsxFormatProvider formatProvider = new XlsxFormatProvider();
					//using (Stream input = new FileStream(filePath, FileMode.Open))
					//{
					//	var book = formatProvider.Import(input);
					//	var sheet = book.Worksheets.First();
					//	var colCount = Data.Columns.Count;
					//	var rowCount = Data.Rows.Count;

					//	FloatingChartShape chartShape = new FloatingChartShape(sheet, new CellIndex(rowCount + 1, 0), new CellRange(
					//		0, 1, rowCount, colCount - 1), SeriesRangesOrientation.Vertical, ChartType.Line)
					//	{
					//		Width = 800,
					//		Height = 600
					//	};
					//	sheet.Charts.Add(chartShape);
					//	SeriesGroup seriesGroup = chartShape.Chart.SeriesGroups.First(); // type BarChart
					//	int seriesCount = seriesGroup.Series.Count(); // 1
					//	SeriesBase series = seriesGroup.Series.First(); // type BarSeries

					//	formatProvider.Export(book, input);
					//}
				}
				catch (Exception ex)
				{
					_ = dialogService.ShowMessageAsync("导出失败", ex.Message);
				}
				try
				{
					Process? p = Process.Start(new ProcessStartInfo
					{
						FileName = filePath,
						UseShellExecute = true
					});
				}
				catch(Exception ex)
				{
                    _ = dialogService.ShowMessageAsync("文件打开失败", ex.Message);
                }
			});
		}
		[RelayCommand(CanExecute = nameof(CanGenerateReport))]
		private async Task GenerateReport()
		{
			SaveFileDialog dialog = new()
			{
				Title = "Excel",
				Filter = "Excel (*.xlsx)|*.xlsx",
			};
			bool? result = dialog.ShowDialog();
            if (result == null || result == false)
			{
				return;
			}
			IsLoading = true;
			await GenerateDocument(dialog.FileName);
			IsLoading = false;
		}

		private bool CanGenerateReport()
		{
			return Data != null && Data.Rows.Count > 0;
		}


		private string GetUnitByChannelName(string name)
		{
            if(Data == null || dataInfo == null)
            {
				return string.Empty;
            }
            int columnIndex = Data.Columns.IndexOf(name);
            if (columnIndex != -1 && columnIndex >= 2)
            {
                columnIndex -= 2;
				if(columnIndex >= dataInfo.Rows.Count)
				{
					return string.Empty;
				}
				DataRow row = dataInfo.Rows[columnIndex];
                string? unit = row["unit"].ToString();
                return unit ?? string.Empty;
            }
            else
            {
				return string.Empty;
            }
        }
		private DaqColor GetColorByChannelName(string name)
		{
			DaqColor color = new DaqColor(255, 0, 0);
            if(Data == null || dataInfo == null)
            {
				return color;
            }
            int columnIndex = Data.Columns.IndexOf(name);
            if (columnIndex != -1 && columnIndex >= 2)
            {
                columnIndex -= 2;
				if(columnIndex >= dataInfo.Rows.Count)
				{
					return color;
				}
				DataRow row = dataInfo.Rows[columnIndex];
				try
				{
					byte r = byte.Parse(row["r"].ToString());
					byte g = byte.Parse(row["g"].ToString());
					byte b = byte.Parse(row["b"].ToString());
					byte a = byte.Parse(row["a"].ToString());
					return new DaqColor(r, g, b, a);
				}
				catch
				{
				}
                return color;
            }
            else
            {
				return color;
            }
        }
		private int GetDigitByChannelName(string name)
		{
            if(Data == null || dataInfo == null)
            {
				return 0;
            }
            int columnIndex = Data.Columns.IndexOf(name);
            if (columnIndex != -1 && columnIndex >= 2)
            {
                columnIndex -= 2;
				if(columnIndex >= dataInfo.Rows.Count)
				{
					return 0;
				}
				DataRow row = dataInfo.Rows[columnIndex];
				return (int)(Int64)row["digits"];
            }
            else
            {
				return 0;
            }
        }

		[ObservableProperty]
		private bool isExportingData;
        public async Task ExportData(string dir, List<string> channelNames, int startMinute, int endMinute, IProgress<double>? progress)
        {
			if (string.IsNullOrWhiteSpace(DataRootDirectory)) return;
			if (channelNames.Count == 0) return;
            if (string.IsNullOrWhiteSpace(dir)) return;
			string rootDirectory = Path.Combine(DataRootDirectory, dir);
            var files = await GetDatabaseFiles(rootDirectory);
            if (files == null || files.Count == 0) return;

            // 创建文件夹选择对话框
            OpenFolderDialog dialog = new()
            {
                Title = "导出数据",
            };
            if (!dialog.ShowDialog() == true)
            {
                return;
            }
			IsExportingData = true;
            try
            {
                int reportFileCount = 1;
                CurrentMetaData = await dataService.ReadMetaData(rootDirectory);
                if (CurrentMetaData == null) return;
                if (CurrentMetaData.Channels == null || CurrentMetaData.Channels.Count == 0) return;
				int count = CurrentMetaData.Channels[0].Properties.Count;
				if (endMinute > count - 1) endMinute = count - 1;

				int totalMinutes = endMinute - startMinute + 1;
				
				for (int i = startMinute; i <= endMinute; i++)
				{
					string filePath = Path.Combine(dialog.FolderName, $"data_part_{(reportFileCount++):D6}.csv");
					if(File.Exists(filePath))
					{
						throw new Exception($"文件{filePath}已存在。");
					}

                    File.Create(filePath).Close();
                    // 写入表头
                    string[] headers = new string[channelNames.Count];
                    for (int j = 0; j < channelNames.Count; j++)
                    {
                        headers[j] = EscapeCsvValue(channelNames[j]);
                    }
                    string str = "Time," + string.Join(',', channelNames) + "\n";
                    await File.AppendAllTextAsync(filePath, str);

                    var dt = await QueryData(i, channelNames);
					if (dt == null) continue;
					StringBuilder sb = new StringBuilder(1024000);
					foreach (System.Data.DataRow row in dt.Rows)
					{
						for (int col = 1; col < dt.Columns.Count; col++)
						{
							if (col != 1)
							{
								sb.Append(',');
							}
							sb.Append(EscapeCsvValue(row[col]?.ToString() ?? ""));
						}
						sb.Append('\n');
					}
					await File.AppendAllTextAsync(filePath, sb.ToString());
                    FileAttributes attributes = File.GetAttributes(filePath);

                    if ((attributes & FileAttributes.ReadOnly) != FileAttributes.ReadOnly)
                    {
                        File.SetAttributes(filePath, attributes | FileAttributes.ReadOnly);
                        Debug.WriteLine($"文件 {filePath} 已设置为只读。");
                    }
					progress?.Report((double)(i - startMinute + 1) / (double)totalMinutes * 100);
                }
			}
            catch (Exception ex)
            {
                await dialogService.ShowMessageAsync("导出失败", ex.Message);
            }
            IsExportingData = false;
        }


        [RelayCommand]
        private void AddItemToChartList(object obj)
		{
			//try
			//{
   //             DateTime startTime = DateTime.Now;
			//	if(obj is ChannelInfo info)
			//	{
   //                 string chName = info.Name;
   //                 var values = Data.AsEnumerable()
   //                     .Select(row => row.Field<float>(chName))
   //                     .ToList();
   //                 var times = Data.AsEnumerable()
   //                     .Select(row => row.Field<DateTime>("update_time"))
   //                     .ToList();
   //                 int count = values.Count;
   //                 ChartChannels ??= new();
   //                 var channel = ChartChannels.FirstOrDefault(ch => ch.Name == chName);
   //                 if (channel == null)
   //                 {
   //                     ChartData[] datas = new ChartData[values.Count];
   //                     for (int i = 0; i < count; i++)
   //                     {
   //                         datas[i].Value = values[i];
   //                         datas[i].Time = times[i];
   //                     }
   //                     ISeries[] series = new ISeries[]
   //                     {
   //                     new LiveChartsCore.SkiaSharpView.LineSeries<float>
   //                     {
   //                         Values = values,
   //                         Stroke = new SolidColorPaint(GetColorByChannelName(chName).ToSKColor()) { StrokeThickness = 1 },
   //                         LineSmoothness = 0,
   //                         GeometryStroke = null,
   //                         GeometryFill = null,
   //                         Fill = null,
   //                         YToolTipLabelFormatter = (chartPoint) =>$"{Math.Round(chartPoint.PrimaryValue, GetDigitByChannelName(chName))}{GetUnitByChannelName(chName)}"
   //                     }
   //                     };
   //                     var model = new ChartChannelDataModel()
   //                     {
   //                         Name = chName,
   //                         Values = new List<ChartData>(datas),
   //                         Series = series
   //                     };
   //                     ChartChannels.Add(model);
   //                 }
   //                 else
   //                 {

   //                 }
   //             }
				
   //         }
			//catch (Exception ex)
			//{
   //             Debug.WriteLine(ex.Message);
   //         }
        }

		[RelayCommand]
		private void OpenLocalData()
		{
            var url = $"http://{settings.SystemSettings.Instance.Device.Address}:8080";
			Debug.WriteLine(url);
			try
            {
                Process.Start(new ProcessStartInfo(url)
                {
                    UseShellExecute = true,
                    Verb = "open"
                });
            }
            catch
            {
                Debug.WriteLine("Failed to open the URL in the default browser.");
            }
        }

		[RelayCommand]
		private void OpenDataStorageSettings()
		{
   //         DataStorageSettingView? view = App.Current.Services.GetService(typeof(DataStorageSettingView)) as DataStorageSettingView;
			//if (view == null) return;
   //         NewDataRootDirectory = DataRootDirectory;
   //         view.Owner = App.Current.MainWindow;
			//view.DataContext = this;
   //         view.ShowDialog();
        }

		[RelayCommand]
		private void SelectFolder()
		{
			
   //         OpenFolderDialog openFolderDialog = new OpenFolderDialog();
   //         openFolderDialog.ShowDialog();
   //         var name = openFolderDialog.FolderName;
			//if(!string.IsNullOrWhiteSpace(name))
			//{
   //             NewDataRootDirectory = name;
   //         }
        }

		[RelayCommand]
		private async Task SaveDataRootDirectory()
		{
			if (string.IsNullOrWhiteSpace(NewDataRootDirectory)) return;
			DataRootDirectory = NewDataRootDirectory;
			settings.SystemSettings.Instance.DataDirectory = DataRootDirectory;
			await settings.SystemSettings.Save();
		}
	}

}
